package edu.hawaii.wattdepotapps.visualizer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.wicket.extensions.yui.calendar.DatePicker;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.markup.html.form.CheckBoxMultipleChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.CSSPackageResource;
import org.apache.wicket.markup.html.JavascriptPackageResource;
import org.apache.wicket.util.template.TextTemplateHeaderContributor;
import org.apache.wicket.extensions.markup.html.form.DateTextField;
import org.wattdepot.client.BadXmlException;
import org.wattdepot.client.MiscClientException;
import org.wattdepot.client.NotAuthorizedException;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.resource.source.jaxb.Source;
import edu.hawaii.wattdepotapps.BasePage;

/**
 * 
 * Creates a multi-selectable Wicket UI component of Source names necessary to generate a Google
 * Visualization of a Source's sensor and interpolated data. The only Wicket component of the
 * VisualizationPage.html is the multi-selectable source menu, the rest is implemented using
 * JavaScript and HTML.
 * 
 * @author Edward Meyer, Kendyll Doi, Bao Huy Ung
 * @version 2.0
 */
public class VisualizationPage extends BasePage {

  /**
   * Makes the page serializable.
   */
  private static final long serialVersionUID = -5719619758599534459L;

  /** Holds the connection to a WattDepot server. */
  private static WattDepotClient client;

  /**
   * Default URI to the public server in WattDepot. Overwritten in Jetty.java if the user specifies
   * another server.
   */
  private static String restURI = "http://server.wattdepot.org:8194/wattdepot/";

  /** Holds and displays input from user date. */
  private DateTextField tfBegDate = new DateTextField("BegDate", "M/d/yyyy");

  /** Holds and displays input from user date. */
  private DateTextField tfEndDate = new DateTextField("EndDate", "M/d/yyyy");

  /**
   * Calls the create methods of the pages interface and connects to wattdepot. Connects to a
   * WattDepot server and calls the create methods to generate the necessary Wicket UI components to
   * generate a Google Visualization of a Source's sensor and interpolated data.
   */
  public VisualizationPage() {

    /**
     * Adds the resource files for the CSS and Javascript for the visualization page through wicket.
     */
    add(CSSPackageResource.getHeaderContribution(VisualizationPage.class, "VisualizationPage.css"));
    add(JavascriptPackageResource.getHeaderContribution("http://www.google.com/jsapi"));

    // Create an IModel object to wrap the HashMap as a component
    // used to substitute variables in the JavaScript.
    IModel<Map<String, Object>> variablesModel = new AbstractReadOnlyModel<Map<String, Object>>() {
      /**
       * Support serialization.
       */
      private static final long serialVersionUID = 1L;
      /** Cached variables; we only need to fill this once. */
      private Map<String, Object> variables;

      @Override
      /** Adds the variable substitutions to the Map and returns it. */
      public Map<String, Object> getObject() {
        if (variables == null) {
          // Add the data uri to the map so it can be substituted in the JavaScript for ${data_uri}.
          this.variables = new HashMap<String, Object>();
          variables.put("rest_uri", restURI);
        }
        return variables;
      }
    };

    // Add the JavaScript to the VisualizationPage and substitution
    // and variables return in variablesModel.
    add(TextTemplateHeaderContributor.forJavaScript(VisualizationPage.class,
        "VisualizationPage.js", variablesModel));

    // Holds the Form for the page.
    Form<String> pageForm = null;
    pageForm = new Form<String>("visualizedForm") {

      /** Support serialization. */
      private static final long serialVersionUID = 1L;
    };

    // To start the Visualization page, connect to the Wattdepot server to query data.
    // Last 2 params are null as they are used for username and password.
    connect(restURI, null, null);

    // Initialize the beginning and ending text fields that will hold their respective dates.
    tfBegDate.setDefaultModel(new Model<Date>());
    tfEndDate.setDefaultModel(new Model<Date>());

    // Debug print statements.
    // System.out.println(session.getId() + ":" + session.getBegDate().toString());
    // System.out.println(session.getId() + ":" + session.getEndDate().toString());

    // Add a new DatePicker which will hold the Beginning Date specified by the user.
    // The user must specify a date in order to proceed with the system.
    tfBegDate.add(new DatePicker());
    tfBegDate.setRequired(true);
    tfBegDate.setDefaultModelObject(new Date());

    // Add a new DatePicker which will hold the Ending Date specified by the user.
    // The user must specify a date in order to proceed with the system.
    tfEndDate.add(new DatePicker());
    tfEndDate.setRequired(true);
    tfEndDate.setDefaultModelObject(new Date());
    // System.out.println("Date: " + new DateTime().toDate());
    // System.out.println("Date as Gregorian Calendar : " + new DateTime().toGregorianCalendar());

    // Add the Beginning and Ending Dates to the form.
    pageForm.add(tfBegDate);
    pageForm.add(tfEndDate);

    // Adds the source checkbox menu to the page form.
    pageForm = createSourceDropDown(pageForm);

    add(pageForm);

  }

  /**
   * Connect method to create connection to a WattDepot server.
   * 
   * @param uri contains a string of the uri to connect to WattDepot.
   * @param username a string of a user on the WattDepot.
   * @param password a string of the user's password to WattDepot.
   * @return a string with output of method.
   */
  private String connect(String uri, String username, String password) {

    try {
      client = new WattDepotClient(uri, username, password);
      // Stop right away if we cannot contact server.
      if (!client.isHealthy()) {
        return "Failed to connect to " + client.getWattDepotUri();
      }
      // Indicate successful connection.
      return "Connected to " + client.getWattDepotUri();
    }
    catch (Exception e) {
      return e.getMessage();
    }
  }

  /**
   * Creates the CheckBoxMultipleSelect of all sources that the client has access to.
   * 
   * @param form Holds the page's form.
   * @return a Form<String> object with objects added.
   */
  private Form<String> createSourceDropDown(Form<String> form) {

    // Holds the list of all sources.
    List<String> sourceList;

    // Attempt to get the list of all sources in the client.
    try {
      sourceList = getClientSources();
    }
    // Server does not allow anonymous/public access.
    catch (NotAuthorizedException e) {
      sourceList = Arrays.asList("No Sources");
      System.err.format("Error: Client %s does not allow public/anonymous access.",
          client.getWattDepotUri());
    }
    // Received bad XML from server.
    catch (BadXmlException e) {
      sourceList = Arrays.asList("No Sources");
      System.err.println("Error: Received bad XML from server.");
    }
    // Any other miscellaneous errors that occur will be caught by MiscClientException.
    catch (MiscClientException e) {
      sourceList = Arrays.asList("No Sources");
      System.err.println("Error: Had problems retrieving sources from server, "
          + "please check your connection.");
    }

    // Initialize the multi-choice checkbox menu field.
    CheckBoxMultipleChoice<String> dropSources =
        new CheckBoxMultipleChoice<String>("Source", sourceList);
    dropSources.setRequired(true);
    // Add the menu to the page.
    form.add(dropSources);

    return form;
  }

  /**
   * Returns a list of strings of all the names in a WattDepot server.
   * 
   * @return A list of String names of all sources.
   * @throws MiscClientException If error is encountered retrieving sources, or some unexpected
   * problem is encountered.
   * @throws BadXmlException If error is encountered unmarshalling the XML from the server.
   * @throws NotAuthorizedException If the client is not authorized to retrieve sources.
   */
  private List<String> getClientSources() throws NotAuthorizedException, BadXmlException,
      MiscClientException {

    // Create a list of all sources in the WattDepot server.
    List<Source> allSources = client.getSources();
    List<String> names = new ArrayList<String>();

    // Loop through each source and add the name to the list.
    for (Source source : allSources) {
      names.add(source.getName());
    }

    // Return all the names in the list.
    return names;
  }

  /**
   * Sets the restURI to connect to.
   * 
   * @param newURI Is the new URI to the server.
   */
  public static void setRestURI(String newURI) {
    restURI = newURI;
  }

}
